/*
 @Name: calender插件
 @Author: ray
 @License: MIT
*/
; rayui.define(function (exports, undef) {
    "use strict";

    var $ = rayui.$,
        $win = $(window),
        $body = $("body"),
        plugName = "calender",
        globalIndex = 1000,
        dataCache = {},
        scrollGap = null,
        regexMatchDate = /(\d{1,4})\/(\d{1,2})\/(\d{1,2})/,
        rayMinDatetime = new Date(1970, 1, 1, 0, 0, 0),
        rayMaxDatetime = new Date(9999, 11, 31, 23, 59, 59),
        activeCss = "cal-active",
        disabledCss = "cal-disabled",
        string2Date = function (str) {
            if (str === undef || str === "" || str.length < 4) return null;
            str = str.replace(/\-/g, "/");
            //避免年份字符串转成GTM+8
            str.length === 4 && str.indexOf('/') === -1 && (str += "/1");
            return new Date(str);
        },
        //只运行一次
        initDefaultOnce = function () {
            //计算浏览横向滚动条的高度和竖向滚动条的宽度
            if (scrollGap !== null) return;
            var $div = $("<div/>").appendTo($("body"));
            $div.width(100).height(100).css("overflow", "scroll");
            var elem = $div[0];
            scrollGap = {
                width: elem.offsetHeight - elem.clientHeight,
                height: elem.offsetWidth - elem.clientWidth
            }
            $div.remove();
        },
        onDocClick = function () {
            $('.rayui-calender-box:visible:not(.rayui-calender-container)').each(function () {
                var index = $(this).data("index"),
                    that = dataCache[index];
                that.options.readOnly ? DealClass.utils.hide.call(that) : that.options.onInputChange();
            });
        },
        DealClass = function (options) {
            var hasFormat = options.format,
                tmp = $.extend(true, {}, DealClass.option);//深度拷贝
            options = this.options = $.extend(tmp, options);//普通拷贝，主要是因为有list对象
            options.eventElem = options.eventElem || options.elem;
            if (typeof options.minDatetime === "string")
                options.minDatetime = string2Date(options.minDatetime) || rayMinDatetime;
            if (typeof options.maxDatetime === "string")
                options.maxDatetime = string2Date(options.maxDatetime) || rayMaxDatetime;
            if (options.maxDatetime < options.minDatetime) {
                rayui.log("最大时间不能小于最小时间");
                return "最大时间不能小于最小时间";
            }
            if (typeof options.selectedDate === "string")
                options.selectedDate = string2Date(options.selectedDate);

            //如果是datetime，强制添加确定按钮
            if (options.type.indexOf("time") >= 0 && options.toolBtns.indexOf("ok") === -1)
                options.toolBtns.push("ok");
            //初始化format
            if (!hasFormat) {
                switch (options.type) {
                    case "year": options.format = "yyyy"; break;
                    case "month": options.format = "yyyy-MM"; break;
                    case "date": options.format = "yyyy-MM-dd"; break;
                    case "time": options.format = "HH:mm:ss"; break;
                    case "datetime": options.format = "yyyy-MM-dd HH:mm:ss"; break;
                }
            }
            //input是否只读
            if ($(options.elem).length !== 0) {
                var inputSet = $(options.elem).attr("readonly");
                inputSet !== undef && (options.readOnly = true);
                options.readOnly && $(options.elem).prop("readonly", true);
            }

            //一周的第一天
            if (options.firstDayOfWeek === 1) {
                var sunday = options.weeks.splice(0, 1);
                options.weeks.push(sunday[0]);
            }
            //设置默认时间
            options.selectedDate && $(options.elem).val(options.selectedDate.format(options.format));

            options.selectedDate = options.selectedDate || new Date();
            DealClass.utils.resetSelecedDate.call(this);
            options.preSelectedDate = options.selectedDate.getTime();

            //初始化
            DealClass.utils.init.call(this);
            return this;
        };

    DealClass.option = {
        type: 'date', //控件类型，支持：year/month/date/time/datetime
        readOnly: false, //是否只读，默认false
        event: "click", //触发方式
        autoPick: true, //是否自动选择
        firstDayOfWeek: 1, //周的第一天 0表示星期日 1表示星期一
        format: 'yyyy-MM-dd', //默认日期格式
        mark: {},//标记
        holiday: {},//节假日+补班
        minDatetime: new Date(1970, 1, 1, 0, 0, 0),
        maxDatetime: new Date(9999, 11, 31, 23, 59, 59),
        animSpeed: 200,//动画
        timeHours: 1,//数值或数值数组
        timeMinutes: 1,//数值或数值数组
        timeSeconds: 1,//数值或数值数组
        toolBtns: ["clear", "now", "ok"],

        //下面是国际化支持参数
        toolBtnTextes: {
            clear: "清除",
            now: "现在",
            ok: "确定"
        },
        weeks: ['日', '一', '二', '三', '四', '五', '六'],
        monthes: ['一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'],//月份
        datetime: ['年', '月', '日', '时', '分', '秒'],
        showTimeText: "选择时间",
        backDateText: "返回日期"

    };

    Number.prototype.toStringRay = function (c) {
        c = c || 1;
        if (c < 2) return this;
        if (this < 10) return "0" + this;
        return this;
    }

    DealClass.utils = {
        init: function () {
            var that = this,
                options = that.options,
                $con = options.container ? $(options.container) : $body;

            options.index = globalIndex;
            dataCache[globalIndex++] = that;

            options.$main = $(['<div class="rayui-calender-box',
                options.container ? ' rayui-calender-container' : '',
                '"',//end class
                ' data-index="' + options.index + '"',
                options.skin ? ' skin="' + options.skin + '"' : '',
                '/>'].join('')).appendTo($con);
            options.$head = $([
                '<div class="rayui-calender-head cal-' + options.type + '">',
                '<span class="year-prev"><i class="ra ra-double-angle-left"></i></span>',
                '<span class="month-prev"><i class="ra ra-single-angle-left"></i></span>',
                '<span class="year-next"><i class="ra ra-double-angle-right"></i></span>',
                '<span class="month-next"><i class="ra ra-single-angle-right"></i></span>',
                '<div class="title">',
                '<span class="title-1"></span>',
                '<span class="title-2"></span>',
                '</div>',
                '</div>'
            ].join('')).appendTo(options.$main);
            options.$yearprev = options.$head.find('.year-prev');
            options.$monthprev = options.$head.find('.month-prev');
            options.$yearnext = options.$head.find('.year-next');
            options.$monthnext = options.$head.find('.month-next');
            options.$title1 = options.$head.find('.title-1');
            options.$title2 = options.$head.find('.title-2');
            options.$content = $('<div class="rayui-calender-content" />').appendTo(options.$main);
            options.$tool = $('<div class="rayui-calender-tool hidden" />').appendTo(options.$main);
            options.$toolbtn = $('<div class="btn-group" />').appendTo(options.$tool);
            options.$showtime = $('<span class="show-time clearfix">&nbsp;</span>').appendTo(options.$tool);

            options.toolBtns.length > 0 && options.$tool.removeClass("hidden");
            DealClass.utils.creYearDom.call(that);
            DealClass.utils.creMonthDom.call(that);
            DealClass.utils.creDateDom.call(that);
            DealClass.utils.creTimeDom.call(that);
            DealClass.utils.creTools.call(that);
            //初始化dom显隐和数据
            DealClass.utils.initDomData.call(that);
            //绑定事件
            DealClass.utils.event.call(this);

        },
        creYearDom: function () {
            var that = this,
                options = that.options;
            if (options.type === "time") return;

            var $content = options.$content,
                selectedDate = options.selectedDate,
                curYear = selectedDate.getFullYear(),
                yearName = options.datetime[0],
                $box = function () {
                    var $d = $content.find(".box-year");
                    if ($d.length > 0) return $d.html("");
                    return $('<ul class="box-year hidden">').appendTo($content);
                }(),
                minYear = options.minDatetime.getFullYear(),
                maxYear = options.maxDatetime.getFullYear();

            var startY = curYear - 7, endY = curYear + 7;
            while (startY <= endY) {
                var cls = startY === curYear ? activeCss : "";
                (startY < minYear || startY > maxYear) && (cls += " " + disabledCss);
                $box.append('<li class="' + cls + '" data-value="' + startY + '">' + (startY++) + yearName + '</li>');
            }
        },
        creMonthDom: function () {
            var that = this,
                options = that.options;
            if (options.type === "year" || options.type === "time") return;

            var $content = options.$content,
                selectedDate = options.selectedDate,
                curYear = selectedDate.getFullYear(),
                curMonth = selectedDate.getMonth(),
                monthName = options.datetime[1],
                minDateTime = new Date(options.minDatetime.getFullYear(), options.minDatetime.getMonth()),
                maxDateTime = new Date(options.maxDatetime.getFullYear(), options.maxDatetime.getMonth()),
                $box = function () {
                    var $d = $content.find(".box-month");
                    if ($d.length > 0) return $d.html("");
                    return $('<ul class="box-month hidden">').appendTo($content);
                }(),
                i = 0;
            while (i < 12) {
                var cls = i === curMonth ? activeCss : "",
                    date = new Date(curYear, i);
                (date < minDateTime || date > maxDateTime) && (cls += " " + disabledCss);
                $box.append(
                    '<li class="' + cls + '" data-value="' + i + '">' + options.monthes[i] + monthName + '</li>');
                i++;
            }
        },
        creDateDom: function () {
            var that = this,
                options = that.options;
            if (options.type.indexOf("date") !== 0) return;

            var $content = options.$content,
                selectedDate = options.selectedDate,
                curYear = selectedDate.getFullYear(),
                curMonth = selectedDate.getMonth(),
                $box = function () {
                    var $d = $content.find(".box-date");
                    if ($d.length > 0) return $d.html("");
                    return $('<table class="box-date hidden"></table>').appendTo($content);
                }(),
                str = [];

            //计算首个第一天
            var start = new Date(curYear, curMonth, 1),
                dayOfWeek = start.getDay(), j,
                minDate = new Date(options.minDatetime.format("yyyy/MM/dd")),
                maxDate = new Date(options.maxDatetime.format("yyyy/MM/dd")),
                minus = options.firstDayOfWeek === 0 ? dayOfWeek : dayOfWeek === 0 ? 6 : dayOfWeek - 1;
            minus !== 0 && start.setDate(start.getDate() - minus);
            options.tdDateStart = new Date(start);
            //表头
            str.push('<thead><tr>');
            for (j = 0; j < 7; j++) {
                str.push('<th>' + options.weeks[j] + '</th>');
            }
            str.push('</tr></thead>');
            var seledDay = selectedDate.getDate();
            for (var i = 0; i < 6; i++) {
                str.push('<tr>');
                for (j = 0; j < 7; j++) {
                    var mm = start.getMonth(),
                        dd = start.getDate(),
                        cls = mm !== curMonth ? "other-month" : "";
                    mm === curMonth && dd === seledDay && (cls += " " + activeCss);
                    (start < minDate || start > maxDate) && (cls += " " + disabledCss);
                    str.push('<td class="' + cls + '" data-value="' + start.format("yyyy/MM/dd") + '">' + dd + '</td>');
                    start.setDate(start.getDate() + 1);
                }
                str.push('</tr>');
            }
            $box.append(str.join(''));
            options.tdDateEnd = new Date(start);
            DealClass.utils.setMark.call(that);
            DealClass.utils.setHoliday.call(that);
        },
        creTimeDom: function () {
            var that = this,
                options = that.options;
            if (!(options.type === "time" || options.type === "datetime")) return;

            var $content = options.$content,
                selectedDate = options.selectedDate,
                year = selectedDate.getFullYear(),
                month = selectedDate.getMonth(),
                dd = selectedDate.getDate(),
                hh = selectedDate.getHours(),
                mm = selectedDate.getMinutes(),
                ss = selectedDate.getSeconds(),
                $box = function () {
                    var $d = $content.find(".box-time");
                    if ($d.length > 0) return $d.html("");
                    return $('<div class="box-time hidden" />').appendTo($content);
                }(),
                str = [], i = 0, a, len, date, val, cls;

            //时分秒标题
            str.push(['<ul class="box-time-title">',
                '<li>' + options.datetime[3] + '</li>',
                '<li>' + options.datetime[4] + '</li>',
                '<li>' + options.datetime[5] + '</li>',
                '</ul>'].join(''));

            //时
            str.push('<div class="no-scroll"><div class="box-hour"><ul>');
            if (typeof options.timeHours === "number") {
                while (i < 24) {
                    cls = i === hh ? activeCss : "";
                    date = new Date(year, month, dd, i);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + cls + '" data-value="' + i + '">' + i.toStringRay(2) + '</li>');
                    i = i + options.timeHours;
                }
            } else {
                a = 0;
                len = options.timeHours.length;
                while (a < len) {
                    val = options.timeHours[a];
                    cls = i === hh ? activeCss : "";
                    date = new Date(year, month, dd, val);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + cls + '" data-value="' + val + '">' + val.toStringRay(2) + '</li>');
                    a++;
                }
            }
            str.push('</ul></div></div>');
            //分
            i = 0;
            str.push('<div class="no-scroll"><div class="box-minute"><ul>');
            if (typeof options.timeMinutes === "number") {
                while (i < 60) {
                    cls = i === mm ? activeCss : "";
                    date = new Date(year, month, dd, hh, i);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + cls + '" data-value="' + i + '">' + i.toStringRay(2) + '</li>');
                    i = i + options.timeMinutes;
                }
            } else {
                a = 0;
                len = options.timeMinutes.length;
                while (a < len) {
                    val = options.timeMinutes[a];
                    cls = i === mm ? activeCss : "";
                    date = new Date(year, month, dd, hh, val);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + cls + '" data-value="' + val + '">' + val.toStringRay(2) + '</li>');
                    a++;
                }
            }
            str.push('</ul></div></div>');
            //秒
            i = 0;
            str.push('<div class="no-scroll"><div class="box-second"><ul>');
            if (typeof options.timeSeconds === "number") {
                while (i < 60) {
                    cls = i === ss ? activeCss : "";
                    date = new Date(year, month, dd, hh, i);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + cls + '" data-value="' + i + '">' + i.toStringRay(2) + '</li>');
                    i = i + options.timeSeconds;
                }
            } else {
                a = 0;
                len = options.timeSeconds.length;
                while (a < len) {
                    val = options.timeSeconds[a];
                    cls = i === ss ? activeCss : "";
                    date = new Date(year, month, dd, hh, mm, val);
                    (date < options.minDateTime || date > options.maxDateTime) && (cls += " " + disabledCss);
                    str.push('<li class="' + (i === ss ? " " + activeCss : "") + '" data-value="' + val + '">' + val.toStringRay(2) +
                        '</li>');
                    a++;
                }
            }
            str.push('</ul></div></div>');
            $box.append(str.join(''));
        },
        creTools: function () {
            var that = this,
                options = that.options,
                $toolbtn = options.$toolbtn,
                selectedDate = options.selectedDate;

            options.type === "datetime" && options.$showtime.html(selectedDate.format('HH:mm:ss'));
            $.each(options.toolBtns, function (a, b) {
                $toolbtn.append('<a class="rayui-btn" data-event="' + b + '">' + options.toolBtnTextes[b] + '</a>');
            });
        },
        resetTimeDom: function () {
            var that = this,
                options = that.options;
            if (!(options.type === "time" || options.type === "datetime")) return;

            DealClass.utils.setShowTime.call(that);
            //循环判断最大最小时间
            var selectedDate = options.selectedDate,
                year = selectedDate.getFullYear(),
                month = selectedDate.getMonth(),
                day = selectedDate.getDate(),
                minTime = new Date(options.minDatetime.format("yyyy/MM/dd HH:00:00")),
                maxTime = new Date(options.maxDatetime.format("yyyy/MM/dd HH:00:00")),
                $hour = options.$content.find(".box-hour"),
                $min = options.$content.find(".box-minute"),
                $sec = options.$content.find(".box-second"),
                topHeight = $hour.height() / 2 + $hour.offset().top,
                flag, nn;
            //时
            nn = selectedDate.getHours();
            $hour.find("li").each(function () {
                var hh = $(this).removeClass(activeCss).data("value"),
                    datetime = new Date(year, month, day, hh);
                flag = datetime < minTime || datetime > maxTime;
                $(this)[flag ? "addClass" : "removeClass"](disabledCss);
                //选中+自动滚动
                hh === nn
                    && $(this).addClass(activeCss)
                    && $hour.scrollTop($(this).offset().top + $hour.scrollTop() - topHeight);
            });
            //分
            nn = selectedDate.getMinutes();
            minTime = new Date(options.minDatetime.format("yyyy/MM/dd HH:mm:00"));
            maxTime = new Date(options.maxDatetime.format("yyyy/MM/dd HH:mm:00"));
            $min.find("li").each(function () {
                var mm = $(this).removeClass(activeCss).data("value"),
                    datetime = new Date(year, month, day, selectedDate.getHours(), mm);
                flag = datetime < minTime || datetime > maxTime;
                $(this)[flag ? "addClass" : "removeClass"](disabledCss);
                //选中+自动滚动
                mm === nn
                    && $(this).addClass(activeCss)
                    && $min.scrollTop($(this).offset().top + $min.scrollTop() - topHeight);
            });
            //秒
            nn = selectedDate.getSeconds();
            $sec.find("li").each(function () {
                var ss = $(this).removeClass(activeCss).data("value"),
                    datetime = new Date(year, month, day, selectedDate.getHours(), selectedDate.getMinutes(), ss);
                flag = datetime < options.minDatetime || datetime > options.maxDatetime;
                $(this)[flag ? "addClass" : "removeClass"](disabledCss);
                //选中+自动滚动
                ss === nn
                    && $(this).addClass(activeCss)
                    && $sec.scrollTop($(this).offset().top + $sec.scrollTop() - topHeight);
            });
        },
        resetSelecedDate: function () {
            var that = this,
                options = that.options;

            if (options.selectedDate < options.minDatetime) {
                options.selectedDate = new Date(options.minDatetime);
            } else if (options.selectedDate > options.maxDatetime) {
                options.selectedDate = new Date(options.maxDatetime);
            }
        },
        initDomData: function () {
            var that = this,
                options = that.options,
                $title1 = options.$title1,
                $title2 = options.$title2,
                $content = options.$content,
                selectedDate = options.selectedDate,
                curMonth = selectedDate.getMonth() + 1,
                yearName = options.datetime[0],
                monthName = options.datetime[1];

            //判断显隐 year/month/date/time/datetime
            switch (options.type) {
                case "year":
                    var startY = $content.find(".box-year>li:first").data("value"),
                        endY = $content.find(".box-year>li:last").data("value");
                    $title1.html(startY + yearName + " - " + endY + yearName);
                    $content.find(">.box-year").removeClass("hidden");
                    break;
                case "month":
                    $title1.html(selectedDate.getFullYear() + yearName);
                    $title2.html(curMonth + monthName);
                    $content.find(">.box-month").removeClass("hidden");
                    break;
                case "date":
                    $title1.html(selectedDate.getFullYear() + yearName);
                    $title2.html(curMonth + monthName);
                    $content.find(">.box-date").removeClass("hidden");
                    break;
                case "time":
                    $title1.html(options.showTimeText);
                    $content.find(">.box-time").removeClass("hidden");
                    break;
                case "datetime":
                    $title1.html(selectedDate.getFullYear() + yearName);
                    $title2.html(curMonth + monthName);
                    $content.find(">.box-date").removeClass("hidden");
                    break;
                default:
                    break;
            }

        },
        initHeadBtnState: function () {
            var that = this,
                options = that.options;
            if (options.type === "time") return;

            var $head = options.$head,
                $prevYear = $head.find('>span.year-prev'),
                $nextYear = $head.find('>span.year-next'),
                $prevMonth = $head.find('>span.month-prev'),
                $nextMonth = $head.find('>span.month-next'),
                selectedDate = options.selectedDate,
                minDate = options.minDatetime,
                maxDate = options.maxDatetime;

            //判断年按钮
            var selYear = selectedDate.getFullYear(),
                minYear = minDate.getFullYear(),
                maxYear = maxDate.getFullYear();

            $prevYear[selYear > minYear ? "removeClass" : "addClass"](disabledCss).mouseover();
            $nextYear[selYear < maxYear ? "removeClass" : "addClass"](disabledCss).mouseover();

            //判断月按钮
            if (options.type === "year") return;
            var selMonth = new Date(selYear, selectedDate.getMonth()),
                minMonth = new Date(minYear, minDate.getMonth()),
                maxMonth = new Date(maxYear, maxDate.getMonth());

            $prevMonth[selMonth > minMonth ? "removeClass" : "addClass"](disabledCss).mouseover();
            $nextMonth[selMonth < maxMonth ? "removeClass" : "addClass"](disabledCss).mouseover();

        },
        initTimeDomOnce: function () {
            var that = this,
                options = that.options;
            //隐藏时间的滚动条
            if (options.timeHasSplit !== true) {
                options.timeHasSplit = true;
                if (!(options.type === "time" || options.type === "datetime")) return;
                initDefaultOnce();
                var width3Split = options.$main.width() / 3;
                options.$main.find('.no-scroll').each(function () {
                    $(this).children(":first").width(width3Split + scrollGap.width);
                });
            }
            //控制时间滚动条滚动到选中的地方
            if (options.timeHasScroll !== true) {
                options.timeHasScroll = true;
                var $timebox = options.$content.find(">.box-time");
                $timebox.find(">.no-scroll>div").each(function () {
                    var $selecd = $(this).find('.' + activeCss);
                    if ($selecd.length === 0) return true;
                    var top = $selecd.offset().top - $(this).offset().top;
                    $(this).scrollTop(top - ($(this).height() / 2));
                });
            }
        },
        setHeadYearMonth: function () {
            var that = this,
                options = that.options;
            options.$title1.html(options.selectedDate.getFullYear() + options.datetime[0]);
            options.$title2.html(options.selectedDate.getMonth() + 1 + options.datetime[1]);
        },
        setMark: function () {
            var that = this,
                options = that.options;

            if (options.type.indexOf("date") !== 0) return;

            var $css = options.$content.find("div.styleMarkCss"),
                key, noMark = true;

            for (key in options.mark) {
                noMark = false;
                break;
            }

            if (noMark) {
                $css.html("");
                return;
            }

            var $content = options.$content,
                $table = $content.find(".box-date>tbody"),
                startDate = options.tdDateStart,
                endDate = options.tdDateEnd,
                cssStyle = [], index = 0;

            $css.length === 0 && ($css = $('<div class="styleMarkCss"/>').appendTo(options.$content));
            for (key in options.mark) {
                var keyTmp = key.replace(/\-/g, "/"),
                    list = keyTmp.match(regexMatchDate),
                    year = parseInt(list[1]),
                    month = parseInt(list[2]) - 1,
                    date = parseInt(list[3]),
                    value = options.mark[key],
                    tip = value.tip || "",
                    color = value.dotColor,
                    $mark = $('<span class="cal-mark"></span>'),
                    start = new Date(startDate), $td;

                while (start < endDate) {
                    if ((year === 0 || start.getFullYear() === year) &&
                        (month === 0 || start.getMonth() === month) &&
                        (start.getDate() === date)) {
                        $td = $table.find('td[data-value="' + start.format("yyyy/MM/dd") + '"]');
                        $td.data("text", $td.text());
                        if ($td.find('.cal-tip').length === 0) {
                            var $tip = $('<span class="cal-tip">' + tip + '</span>');
                            if (tip === "") $tip.text($td.text()).addClass('no-tip');
                            $td.html($tip);
                        }
                        //右上角添加标记
                        $td.append($mark);
                        if (color) {
                            $mark.addClass('mark-index-' + index);
                            cssStyle.push('.rayui-calender-box .mark-index-' + (index++) + '{');
                            cssStyle.push('background-color:' + color + ';');
                            cssStyle.push('}');
                        }
                    }
                    start.setDate(start.getDate() + 1);
                }
            }
            var cssstring = cssStyle.join('');
            cssstring !== "" && $css.html('<style>' + cssstring + '</style>');
        },
        setHoliday: function () {
            var that = this,
                options = that.options;

            if (options.type.indexOf("date") !== 0) return;

            var key, noHoli = true;
            for (key in options.holiday) {
                noHoli = false;
                break;
            }

            if (noHoli) {
                return;
            }

            var $content = options.$content,
                $table = $content.find(".box-date>tbody"),
                startDate = options.tdDateStart,
                endDate = options.tdDateEnd;

            for (key in options.holiday) {
                var keyTmp = key.replace(/\-/g, "/"),
                    list = keyTmp.match(regexMatchDate),
                    year = parseInt(list[1]),
                    month = parseInt(list[2]) - 1,
                    date = parseInt(list[3]),
                    value = options.holiday[key],
                    tip = value.tip || "",
                    type = value.type === 1 ? 1 : 0,
                    start = new Date(startDate), $td;

                while (start < endDate) {
                    if ((year === 0 || start.getFullYear() === year) &&
                        (month === 0 || start.getMonth() === month) &&
                        (start.getDate() === date)) {
                        $td = $table.find('td[data-value="' + start.format("yyyy/MM/dd") + '"]');
                        //如果没有mark的tip则添加holiday的tip，mark的tip为主
                        var $tip = $td.find('.cal-tip');
                        if ($tip.length === 0) {
                            $tip = $('<span class="cal-tip">' + tip + '</span>');
                            if (tip === "") $tip.text($td.text()).addClass('no-tip');
                            $td.html($tip);
                        } else {
                            //判断tip值
                            if (tip !== "" && $tip.text() === $td.data("text"))
                                $tip.text(tip).removeClass("no-tip");
                        }
                        //左上角添加holiday标记
                        var $holiday = $('<span class="ra cal-holiday-' + type + '"></span>');
                        $td.append($holiday);
                    }
                    start.setDate(start.getDate() + 1);
                }
            }
        },
        setShowTime: function () {
            var that = this,
                options = that.options,
                $showtime = options.$showtime;
            if (options.type !== "datetime") return;
            $showtime.html($showtime.hasClass('cal-clicked') ? options.backDateText : options.selectedDate.format('HH:mm:ss'));
        },
        show: function () {
            var that = this,
                options = that.options,
                $main = options.$main;

            rayui.toggleDocEvt();

            options.ishide = false;
            //判断上面显示还是下面显示
            if (options.eventElem) {
                var $input = $(options.elem || options.eventElem),
                    inputOffset = $input.offset(),
                    dataWidth = $main.outerWidth(),
                    dataHeight = $main.outerHeight(),
                    inputHeight = $input.outerHeight(),
                    winWidth = $win.width(),
                    winHeight = $win.height(),
                    blowWidth = winWidth + $win.scrollLeft() - inputOffset.left - 5,
                    blowHeight = winHeight + $win.scrollTop() - inputOffset.top - inputHeight,
                    cssOpt = {
                        left: blowWidth < dataWidth ? inputOffset.left + blowWidth - dataWidth : inputOffset.left,
                        top: "auto",
                        bottom: "auto"
                    };
                //上下显示，不要统一改为top，显示动画
                if (blowHeight < dataHeight) {
                    //上面显示
                    cssOpt.bottom = winHeight - inputOffset.top + 3;
                    //cssOpt.bottom = $body.height() - offsetInput.top + 3;
                } else {
                    cssOpt.top = inputOffset.top + inputHeight + 3;
                }
                $main.css(cssOpt);
            }
            $main.slideDown(options.animSpeed);
            //不要放在slideDown结束后，体验不好
            options.type === "time" && DealClass.utils.initTimeDomOnce.call(that);
            typeof options.onShow === "function" && options.onShow();
        },
        hide: function (isremove) {
            var that = this,
                options = that.options;

            //已经隐藏不再隐藏
            if (options.ishide) return;
            options.ishide = true;
            //这里需要把样式重置一下
            options.$main.find(".cal-clicked").click();

            //需要判断是否选择
            if (typeof options.onPick === "function") {
                if (options.preSelectedDate - options.selectedDate.getTime() !== 0) {
                    options.preSelectedDate = options.selectedDate.getTime();
                    options.onPick(options.inputText);
                }
            }
            typeof options.onHide === "function" && options.onHide.call(that);
            if (isremove) {
                //删除原有控件，重新渲染
                delete dataCache[options.index];
                options.$main.remove();
                DealClass.utils.init.call(that);
            } else {
                //指定容器内的不隐藏
                if (!options.container) options.$main.hide();
            }
        },
        event: function () {
            var that = this,
                options = that.options,
                $content = options.$content,
                $input = $(options.elem),
                isInputOrArea = $input.prop("tagName") === "TEXTAREA" ? true :
                    ($input.prop("tagName") === "INPUT" && $input.prop("type").toUpperCase() === "TEXT") ? true : false,
                $yearbox = $content.find(">.box-year"),
                $monthbox = $content.find(">.box-month"),
                getInputValue = function () {
                    return $input[isInputOrArea ? "val" : "html"]();
                },
                setInputValue = function (type) {
                    //type:1年2月3日10清除11现在12确定
                    options.inputText = type === 10 ? "" : options.selectedDate.format(options.format);
                    $input[isInputOrArea ? "val" : "html"](options.inputText);

                    if (type === 11) {
                        delete dataCache[options.index];
                        options.$main.remove();
                        DealClass.utils.init.call(that);
                        return;
                    }
                },
                setInputValueAndHide = function (type) {
                    setInputValue(type);
                    //智能判断是否隐藏
                    if (type > 9) {
                        DealClass.utils.hide.call(that);
                    } else if (options.autoPick) {
                        if ((options.type === "year" && type === 1) ||
                            (options.type === "month" && type === 2) ||
                            (options.type === "date" && type === 3)) {
                            DealClass.utils.hide.call(that);
                        }
                    }
                };

            //触发条件
            !$(options.eventElem).data("bindEvent") && $(options.eventElem).on(options.event, function (e) {
                isInputOrArea && $input.focus();
                options.preInput = getInputValue();
                DealClass.utils.show.call(that);
                e.stopPropagation();
            }).data("bindEvent", true);

            //文本框输入
            !options.readOnly && !$input.data("bindEvent1") &&
                $input.on("keypress." + plugName, function (ev) {
                    //只允许输入0-9(49-57)/(47)-(45):(58)空格(32)回车(13)，删除(无)，delete(无)
                    var e = ev || event, keycode = e.keyCode || e.which || e.charCode;
                    //rayui.log(keycode);
                    if (keycode === 13) {
                        options.onInputChange();
                        return true;
                    }
                    if (keycode === 32) {
                        if ($input.val() === "" || $input.val().indexOf(' ') >= 0) return false;
                        return true;
                    }
                    if (!(keycode > 46 && keycode < 58 || keycode === 45 || keycode === 58)) return false;
                }).on("click." + plugName, function (e) { e.stopPropagation(); }).data("bindEvent1", true) &&
                (options.onInputChange = function () {
                    var input = getInputValue();
                    if (input === options.preInput) {
                        DealClass.utils.hide.call(that);
                        return;
                    }
                    var datetime = string2Date(input);
                    if (datetime === null || isNaN(datetime))
                        datetime = new Date();

                    options.selectedDate = datetime;
                    DealClass.utils.resetSelecedDate.call(that);
                    setInputValue(12);
                    DealClass.utils.hide.call(that, true);
                });

            //控制时间滚动条滚动到选中的地方
            options.$main.is(":visible") && DealClass.utils.show.call(that);

            //初始化按钮状态
            DealClass.utils.initHeadBtnState.call(that);

            //标题：上一年下一年
            options.$head.on("click.year" + plugName, '>span[class^="year-"]:not(.' + disabledCss + ')', function () {
                var isprev = $(this).hasClass("year-prev"),
                    yearName = options.datetime[0],
                    isyearboxshow = options.$content.find('.box-year:visible').length === 1,
                    gap = isyearboxshow ? 15 : 1;
                options.selectedDate.setFullYear(gap * (isprev ? -1 : 1) + options.selectedDate.getFullYear());
                DealClass.utils.resetSelecedDate.call(that);
                //判断按钮状态
                DealClass.utils.initHeadBtnState.call(that);
                DealClass.utils.creYearDom.call(that);
                //重置月份和日期
                DealClass.utils.creMonthDom.call(that);
                DealClass.utils.creDateDom.call(that);
                DealClass.utils.resetTimeDom.call(that);
                //设置显示值
                if (options.type === "time") return;
                if (options.type === "year") {
                    var startY = $content.find(".box-year>li:first").data("value"),
                        endY = $content.find(".box-year>li:last").data("value");
                    options.$title1.html(startY + yearName + " - " + endY + yearName);
                } else {
                    DealClass.utils.setHeadYearMonth.call(that);
                }
                DealClass.utils.setShowTime.call(that);
            });

            //标题：上一月下一月
            options.$head.on("click.month" + plugName, '>span[class^="month-"]:not(.' + disabledCss + ')', function () {
                var isprev = $(this).hasClass("month-prev");
                options.selectedDate.setMonth((isprev ? -1 : 1) + options.selectedDate.getMonth());
                DealClass.utils.resetSelecedDate.call(that);
                //判断按钮状态
                DealClass.utils.setHeadYearMonth.call(that);
                DealClass.utils.initHeadBtnState.call(that);
                //重置日期
                DealClass.utils.creDateDom.call(that);
                DealClass.utils.resetTimeDom.call(that);
                DealClass.utils.setShowTime.call(that);
            });

            //标题：点击年月
            (options.type === "month" || options.type === "date" || options.type === "datetime") &&
                options.$head.on("click.title" + plugName, '>.title>span[class^=title-]', function () {
                    var isyear = $(this).hasClass("title-1"),
                        $box = options.$content.find(isyear ? '.box-year' : '.box-month');
                    $(this).toggleClass("cal-clicked");
                    options.type !== (isyear ? "year" : "month") && $box.toggleClass('hidden');
                });

            //内容：点击年
            $yearbox.on("click." + plugName, 'li:not(.' + disabledCss + ')', function () {
                var value = $(this).data("value");
                options.selectedDate.setFullYear(value);
                //判断按钮状态
                DealClass.utils.initHeadBtnState.call(that);
                $(this).addClass(activeCss).siblings('.' + activeCss).removeClass(activeCss);
                if (options.type !== "year") {
                    options.$title1.toggleClass("cal-clicked").html($(this).html());
                    $yearbox.addClass('hidden');
                }

                //重置月份和日期
                DealClass.utils.creMonthDom.call(that);
                DealClass.utils.creDateDom.call(that);
                DealClass.utils.resetTimeDom.call(that);
                DealClass.utils.setShowTime.call(that);
                setInputValueAndHide(1);
            });

            //内容：点击月
            $monthbox.on("click." + plugName, 'li:not(.' + disabledCss + ')', function () {
                var value = $(this).data("value");
                $(this).addClass(activeCss).siblings('.' + activeCss).removeClass(activeCss);
                options.selectedDate.setMonth(value);
                //判断按钮状态
                DealClass.utils.initHeadBtnState.call(that);
                if (options.type !== "month") {
                    options.$title2.toggleClass("cal-clicked").html(value + 1 + options.datetime[1]);
                    $monthbox.addClass('hidden');
                }
                //重置日期
                DealClass.utils.creDateDom.call(that);
                DealClass.utils.resetTimeDom.call(that);
                DealClass.utils.setShowTime.call(that);
                setInputValueAndHide(2);
            });

            //内容：点击日
            $content.find(">.box-date").on("click." + plugName, 'td:not(.' + disabledCss + ')', function () {
                var value = $(this).data("value"); //2018/01/01
                options.selectedDate = string2Date(value + " " + options.selectedDate.format("HH:mm:ss"));

                $(this).closest('tbody').find('.' + activeCss).removeClass(activeCss);
                $(this).addClass(activeCss);
                //重置日期
                if ($(this).hasClass("other-month")) {
                    DealClass.utils.creYearDom.call(that);
                    DealClass.utils.creMonthDom.call(that);
                    DealClass.utils.creDateDom.call(that);
                    DealClass.utils.initDomData.call(that);
                }

                DealClass.utils.resetTimeDom.call(that);
                DealClass.utils.setShowTime.call(that);
                setInputValueAndHide(3);
            });

            //内容：点击时间
            $content.find(">.box-time").on("click." + plugName, 'li:not(.' + disabledCss + ')', function () {
                var value = $(this).data("value"), type = $(this).closest('div').prop('class').replace('box-', '');
                if (type === "hour") options.selectedDate.setHours(value);
                else if (type === "minute") options.selectedDate.setMinutes(value);
                else if (type === "second") options.selectedDate.setSeconds(value);
                DealClass.utils.resetTimeDom.call(that);
            });

            //工具栏：点击时间
            options.$showtime.on("click." + plugName, function () {
                var $box = $content.find(">.box-time");
                $(this).toggleClass("cal-clicked");
                $box.toggleClass('hidden');
                DealClass.utils.initTimeDomOnce.call(that);
                DealClass.utils.setShowTime.call(that);
            });

            //工具栏：点击按钮
            options.$toolbtn.on("click." + plugName, '.rayui-btn', function (e) {
                e.stopPropagation();
                var type = $(this).data("event");//type: clear,now,ok
                if (type === "clear") {
                    setInputValueAndHide(10);
                } else if (type === "now") {
                    options.selectedDate = new Date();
                    DealClass.utils.resetSelecedDate.call(that);
                    setInputValueAndHide(11);
                } else if (type === "ok") {
                    setInputValueAndHide(12);
                }
            });

            //阻止冒泡
            options.$main.click(function (e) { e.stopPropagation(); });
        },
        on: function (event, func) {
            if (typeof event !== "string" || typeof func !== "function") return;
            var that = this,
                options = that.options;

            if (event === "pick") options.onPick = func;
            else if (event === "show") options.onShow = func;
            else if (event === "hide") options.onHide = func;
        }
    }

    rayui.bindDocEvt(plugName, onDocClick);

    DealClass.prototype = {
        on: function (event, callback) {
            DealClass.utils.on.call(this, event, callback);
            return this;
        },
        show: function () {
            DealClass.utils.show.call(this);
            return this;
        },
        hide: function () {
            DealClass.utils.hide.call(this);
            return this;
        },
        setMinDatetime: function (datetime) {
            datetime = (datetime || "") === "" ? rayMinDatetime : string2Date(datetime);
            if (datetime === null) return this;
            var that = this, options = that.options;
            if (datetime > options.maxDatetime) {
                rayui.log("最小时间不能大于已设置的最大时间");
                return this;
            }
            if (options.minDatetime - datetime === 0) return this;
            options.minDatetime = datetime;
            DealClass.utils.resetSelecedDate.call(that);

            //重新渲染
            DealClass.utils.creYearDom.call(that);
            DealClass.utils.creMonthDom.call(that);
            DealClass.utils.creDateDom.call(that);
            DealClass.utils.resetTimeDom.call(that);
            //初始化按钮状态
            DealClass.utils.setHeadYearMonth.call(that);
            DealClass.utils.initHeadBtnState.call(that);
            return this;
        },
        setMaxDatetime: function (datetime) {
            datetime = (datetime || "") === "" ? rayMaxDatetime : string2Date(datetime);
            if (datetime === null) return this;
            var that = this, options = that.options;
            if (datetime < options.minDatetime) {
                rayui.log("最大时间不能小于已设置的最小时间");
                return this;
            }
            if (options.maxDatetime - datetime === 0) return this;
            options.maxDatetime = datetime;
            DealClass.utils.resetSelecedDate.call(that);

            //重新渲染
            DealClass.utils.creYearDom.call(that);
            DealClass.utils.creMonthDom.call(that);
            DealClass.utils.creDateDom.call(that);
            DealClass.utils.resetTimeDom.call(that);
            //初始化按钮状态
            DealClass.utils.setHeadYearMonth.call(that);
            DealClass.utils.initHeadBtnState.call(that);
            return this;
        },
        addMark: function (marks) {
            marks = marks || null;
            if (marks === null || typeof marks !== "object") return this;
            $.extend(this.options.mark, marks);
            DealClass.utils.creDateDom.call(this);
            return this;
        },
        removeMark: function (markstrs) {
            typeof markstrs === "string" && (markstrs = [markstrs]);
            var that = this, mark = that.options.mark;
            $.each(markstrs, function (a, b) {
                delete mark[b];
            });
            DealClass.utils.creDateDom.call(that);
            return this;
        },
        clearMark: function () {
            this.options.mark = {};
            DealClass.utils.creDateDom.call(this);
            return this;
        },
        addHoliday: function (holidays) {
            holidays = holidays || null;
            if (holidays === null || typeof holidays !== "object") return this;
            $.extend(this.options.holiday, holidays);
            DealClass.utils.creDateDom.call(this);
            return this;
        },
        removeHoliday: function (holidaystrs) {
            typeof holidaystrs === "string" && (holidaystrs = [holidaystrs]);
            var that = this, holiday = that.options.holiday;
            $.each(holidaystrs, function (a, b) {
                delete holiday[b];
            });
            DealClass.utils.creDateDom.call(that);
            return this;
        },
        clearHoliday: function () {
            this.options.holiday = {};
            DealClass.utils.creDateDom.call(this);
            return this;
        }
    }

    var calender = {
        options: DealClass.option,
        render: function (options) {
            var $con = $(options.container);
            if ($con.length === 0) {
                if ($(options.elem).length === 0) {
                    return "DOM对象不存在";
                }
            }
            return new DealClass(options);
        }
    };

    exports(plugName, calender);
}, rayui.jsAsync());
